package gae.testjig.server;

import java.util.ConcurrentModificationException;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Transaction;

public class ConstraintExample {

	static private Key seatsRootKey;
	static {
		DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
		Entity rootEntity = new Entity("SeatsRoot", "seats_root_key_name"); // we want only one of these entities to ever exist
		ds.put(rootEntity);
		seatsRootKey = rootEntity.getKey();
	}
	
	public void reserveSeat(String ownerName, String seatId) throws DuplicateException {
		for (int i=0;i<10;i++) {
			try {
				reserveSeatAttempt(ownerName, seatId);
				return; // we get here if reservation succeeds
			}
			catch (ConcurrentModificationException cme) {
				// stay in the loop and try again.
			}
			// you could use another backoff algorithm here rather than 100ms each time.
			try { Thread.sleep(100); } catch (InterruptedException e) {}
		}
		throw new ConcurrentModificationException("failed to reserve seat "+seatId);
	}

	private void reserveSeatAttempt(String ownerName, String seatId) throws DuplicateException, ConcurrentModificationException {
		DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
		Transaction txn = datastore.beginTransaction();
		try {
			Query testExistsQuery = new Query("Seat");
			testExistsQuery.setAncestor(seatsRootKey);
			testExistsQuery.addFilter("seatId", Query.FilterOperator.EQUAL, seatId);

			Entity exists = datastore.prepare(txn, testExistsQuery).asSingleEntity();
			if ( exists != null ) {
				throw new DuplicateException("seatId "+seatId+" already exists.");
			} else {
				Entity seatEntity = new Entity("Seat", seatId, seatsRootKey);
				seatEntity.setProperty("ownerName", ownerName);
				seatEntity.setProperty("seatId", seatId);
				seatEntity.setProperty("timeStamp", System.currentTimeMillis());
				datastore.put(txn, seatEntity);
				txn.commit(); // throws java.util.ConcurrentModificationException if entity group was modified by other thread
			}
		}
		finally {
			if (txn.isActive()) {
		        txn.rollback();
		    }
		}
	}

}
